#include"head.h"
#include"add_friends.h"
#define LOGPORT 50021
#define COMPORT 50022
static int sockfd;
static int logfd;
static int usernum = 0;
pthread_mutex_t userMutex = PTHREAD_MUTEX_INITIALIZER;


sem_t usersUpdate;
clients *user;
/*========调用的函数=======*/
static void init_clients(void);
static void *show_users(void *arg);
static void add_clients(clients *newuser);
static void del_user(clients *duser);
static clients * find_user(struct sockaddr_in *addr);
static void *client_handler(void *arg);
static void send_all(int sender_connfd, const char *msg);
static void privateTalk(clients *sender,const char *username, const char *msg);
static void *handle_logconn(void *arg);
void save_history(char *filename,char *msg);
/*===========================*/
//初始化链表
static void init_clients(void)
{
    user = calloc(1,sizeof(clients));
    if (user == NULL)
    {
        perror("用户初始化失败");
        exit(1);
    }
    user->friends = calloc(1,sizeof(friends_t));
    user->friends->next = user->friends;
    user->friends->prev = user->friends;
    user->next = user;
    user->prev = user;
}

static void *show_users(void *arg)
{
    while (1)
    {
        sem_wait(&usersUpdate); // 等待更新
        pthread_mutex_lock(&userMutex);
        printf("\n=== 当前在线用户:%d人 ===\n",usernum);
        bool empty = true;
        clients *p;
        for (p = user->next; p != user; p = p->next)
        {

            printf("用户: %s [%s:%hu]\n", p->name,
                                         inet_ntoa(p->addr.sin_addr),
                                         ntohs(p->addr.sin_port));
            
            empty = false;
        }
        if (empty)
            printf("当前无用户在线\n");
        printf("====================\n\n");
        pthread_mutex_unlock(&userMutex);
    }
}


//添加新用户
static void add_clients(clients *newuser)
{
    //尾插法添加到链表
    newuser->friends = calloc(1, sizeof(friends_t));
    newuser->friends->next = newuser->friends;
    newuser->friends->prev = newuser->friends;
    newuser->next = user;
    newuser->prev = user->prev;
    user->prev->next = newuser;
    user->prev = newuser;
    usernum++;
}

// 删除用户
static void del_user(clients *duser)
{
    free(duser->friends);
    duser->next->prev = duser->prev;
    duser->prev->next = duser->next;
    duser->next = NULL;
    duser->prev = NULL;
}

//查找指定用户
static clients * find_user(struct sockaddr_in *addr)
{
    clients *tmp;
    for ( tmp = user->next;tmp != user;tmp = tmp->next)
    {
        if ((tmp->addr.sin_addr.s_addr == addr->sin_addr.s_addr) && (tmp->addr.sin_port == addr->sin_port))
        {
            return tmp;
        }
        
    }
    return NULL;
}
static void *client_handler(void *arg)
{
    int connfd = *(int *)arg;
    free(arg);
    struct sockaddr_in clientAddr;
    socklen_t len = sizeof(clientAddr);
    getpeername(connfd, (struct sockaddr *)&clientAddr, &len);

    // 读取用户名
    char name[20] = {0};
    if (recv(connfd, name, sizeof(name), 0) <= 0) 
    {
        close(connfd);
        return NULL;
    }
    pthread_mutex_lock(&userMutex);
    // 重名检测
    clients *p;
    for (p = user->next; p != user; p = p->next)
    {
        if (strncmp(p->name, name,strlen(name)) == 0 )
        {
            char *msg = "该用户名已登录，请勿重复登录\n";
            send(connfd, msg, strlen(msg), 0);
            close(connfd);
            pthread_mutex_unlock(&userMutex);
            return NULL;
        }
    }
    pthread_mutex_unlock(&userMutex);
    // 打印上线提醒
    printf("用户：%s [%s:%hu] 上线\n", name,
           inet_ntoa(clientAddr.sin_addr),
           ntohs(clientAddr.sin_port));

    // 添加到链表
    clients *newuser = calloc(1, sizeof(clients));
    strncpy(newuser->name, name, sizeof(newuser->name));
    newuser->connfd = connfd;
    newuser->addr = clientAddr;
    
    //初始化好友链表（防段错误关键）
    newuser->friends = calloc(1, sizeof(friends_t));
    newuser->friends->next = newuser->friends;
    newuser->friends->prev = newuser->friends;

    pthread_mutex_lock(&userMutex);
    add_clients(newuser);
    load_friends(newuser,"friends.txt");
    pthread_mutex_unlock(&userMutex);
    sem_post(&usersUpdate);

    // 接收消息并转发
    char buf[512];
    int n;
    while ((n = recv(connfd, buf, sizeof(buf), 0)) > 0) //只要对面没断开，就算不发消息，n始终 > 0
    {
        buf[n] = '\0';
        char *msg = NULL;
        // 去除首尾空白字符
        char *p = buf;
        while (*p && isspace(*p)) p++;  // 跳过前导空格
        if (*p == '\0') continue;       // 若全是空格或换行符，则跳过此次循环
        printf("收到[%s]消息: %s\n", name, buf);
        char chat_history[1024];
        sprintf(chat_history,"收到[%s]消息: %s\n", name, buf);
        save_history("./chat_history.txt",chat_history);
        if (strncmp(buf, "addf:", 5) == 0)
        {
            char *add_name = buf+5;
            bool found = false;
            printf("已向对面发送添加好友请求\n");
            clients *p;
            for(p = user->next; p != user; p = p->next)
            {
                if(strncmp(p->name,add_name,strlen(add_name)-1) == 0)
                {
                    char fullmsg[512];
                    snprintf(fullmsg, sizeof(fullmsg), "[%s]:向你发起了好友申请，[y:用户名]同意,[n:用户名]拒绝\n", newuser->name);
                    write(p->connfd, fullmsg, strlen(fullmsg));
                    found = true;
                }
            }
            // 找不到信息接收者，通知发送方接收者可能已下线
            if (!found)
             {
                // 发送更明确的错误消息
                char errmsg[100];
                snprintf(errmsg, sizeof(errmsg), "错误：用户 '%s' 不存在或已离线\n", add_name);
                write(newuser->connfd, errmsg, strlen(errmsg));
            }
        }
        else if (strncmp(buf, "showf", 5) == 0) 
        {
            show_friends(newuser);
        }
        else if (strncmp(buf, "y:", 2) == 0) 
        {
            char *friend_name = buf + 2;
            add_friend(newuser, friend_name);
            save_friends("friends.txt");
            // 查找发起人并添加对方为好友
            clients *p;
            for (p = user->next; p != user; p = p->next) {
                if (strncmp(p->name, friend_name,strlen(friend_name)-1) == 0) {
                    add_friend(p, newuser->name);
                    send(p->connfd, "对方已同意添加你为好友\n", strlen("对方已同意添加你为好友\n"), 0);
                    break;
                }
            }
        }
        else if (strncmp(buf, "n:", 2) == 0) {
            char *friend_name = buf + 2;
            clients *p;
            for (p = user->next; p != user; p = p->next) {
                if (strncmp(p->name, friend_name,strlen(friend_name)-1) == 0) {
                    send(p->connfd, "对方拒绝了你的好友申请\n", strlen("对方拒绝了你的好友申请\n"), 0);
                    break;
                }
            }
        }
        else if ((msg=strchr(buf, ':')) != NULL)
        {
            char *target_name = strndup(buf, msg - buf);
            char *message = msg + 1;
            privateTalk(newuser,target_name,message);
        }
        else
            send_all(connfd,buf);
    }

    // 用户断开
    printf("用户：%s [%s:%hu] 下线\n", name,
           inet_ntoa(clientAddr.sin_addr),
           ntohs(clientAddr.sin_port));

    close(connfd);
    pthread_mutex_lock(&userMutex);
    del_user(newuser);
    pthread_mutex_unlock(&userMutex);
    free(newuser);
    usernum--;
    sem_post(&usersUpdate);

    return NULL;
}

// 将来自sender的TCP消息msg群发给其他所有人
static void send_all(int sender_connfd, const char *msg)
{
    clients *p;
    pthread_mutex_lock(&userMutex);
    for (p = user->next; p != user; p = p->next)
    {
        if (p->connfd == sender_connfd)
            continue; // 不给自己发

        send(p->connfd, msg, strlen(msg), 0); // TCP 发消息
    }
    pthread_mutex_unlock(&userMutex);
}

// 私聊消息
static void privateTalk(clients *sender,const char *username, const char *msg)
{
    bool found = false;
    clients *p;
    for(p = user->next; p != user; p = p->next)
    {
        if(strncmp(p->name,username,strlen(username)) == 0)
        {
            char fullmsg[512];
            snprintf(fullmsg, sizeof(fullmsg), "[私聊]%s:%s", sender->name, msg);
            write(p->connfd, fullmsg, strlen(fullmsg));
            found = true;
            return;
        }
    }
    // 找不到信息接收者，通知发送方接收者可能已下线
    if (!found) {
        // 发送更明确的错误消息
        char errmsg[100];
        snprintf(errmsg, sizeof(errmsg), "错误：用户 '%s' 不存在或已离线\n", username);
        write(sender->connfd, errmsg, strlen(errmsg));
    }
    // send(sender->connfd, "x", 1, MSG_OOB);
}
static void *handle_logconn(void *arg)
{
    int logfd = *(int *)arg;

    Listen(logfd, 10); 

    while (1)
    {
        struct sockaddr_in cliaddr;
        socklen_t len = sizeof(cliaddr);
        int conn = Accept(logfd, (struct sockaddr *)&cliaddr, &len);
        if (conn < 0)
        {
            continue;
        }

        char buf[32] = {0};
        int n = recv(conn, buf, sizeof(buf), 0);
        if (n > 0)
        {
            buf[n] = '\0';
            printf("登录端口收到命令: %s\n", buf);

            // 处理 "out" 断开
            if (strcmp(buf, "out") == 0)
            {
                pthread_mutex_lock(&userMutex);
                clients *p = find_user(&cliaddr);
                if (p)
                {
                    printf("用户 %s 手动登出\n", p->name);
                    close(p->connfd);
                    del_user(p);
                    free(p);
                    sem_post(&usersUpdate);
                }
                pthread_mutex_unlock(&userMutex);
            }
        }
        close(conn);
    }

    return NULL;
}
void save_history(char *filename,char *msg)
{
    FILE *fp = fopen(filename,"a");
    if (fp == NULL)
    {
        perror("fp open fail");
        exit(0);
    }
    fprintf(fp,"%s",msg);
    fclose(fp);
}

int main(int argc, char *argv[])
{
    sockfd = Socket(AF_INET, SOCK_STREAM, 0);
    logfd = Socket(AF_INET, SOCK_STREAM, 0);
    struct sockaddr_in addr;
    socklen_t addrlen = sizeof(addr);
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    addr.sin_port = htons(LOGPORT);
    //
    int on = 1;
    Setsockopt(logfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));

    Bind(logfd, (struct sockaddr *)&addr, sizeof(addr));

    addr.sin_port = htons(COMPORT);
    Setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
    Bind(sockfd, (struct sockaddr *)&addr, sizeof(addr));
    Listen(sockfd, 10);
    printf("等待用户连接...\n");
    init_clients();
    sem_init(&usersUpdate, 0, 0);

    pthread_t t1;
    pthread_create(&t1, NULL, show_users, NULL);
    pthread_t t2;
    pthread_create(&t2, NULL, handle_logconn, &logfd);

    while (1) 
    {
        struct sockaddr_in clientAddr;
        socklen_t addrlen = sizeof(clientAddr);
        int *connfd = malloc(sizeof(int));
        *connfd = Accept(sockfd, (struct sockaddr *)&clientAddr, &addrlen);
        if (*connfd < 0)
        {
            continue;
        }
        pthread_t tid;
        pthread_create(&tid, NULL, client_handler, connfd);
        pthread_detach(tid);  // 自动资源回收
    }
    return 0;
}

